﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Input;

namespace ARR.UI.Helpers
{
    //public class EventToCommand : TriggerAction<FrameworkElement>
    //{
    //    // Fields
    //    private object _commandParameterValue;
    //    private bool? _mustToggleValue;
    //    private bool _passEventArgsToCommand;
    //    public static readonly DependencyProperty CommandParameterProperty;
    //    public static readonly DependencyProperty CommandProperty;
    //    public static readonly DependencyProperty MustToggleIsEnabledProperty;

    //    // Methods
    //    static EventToCommand()
    //    {
    //        CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventToCommand), new PropertyMetadata(null, delegate(DependencyObject s, DependencyPropertyChangedEventArgs e)
    //        {
    //            EventToCommand command = s as EventToCommand;
    //            if ((command != null) && (command.AssociatedObject != null))
    //            {
    //                command.EnableDisableElement();
    //            }
    //        }));
    //        CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommand), new PropertyMetadata(null, delegate(DependencyObject s, DependencyPropertyChangedEventArgs e)
    //        {
    //            OnCommandChanged(s as EventToCommand, e);
    //        }));
    //        MustToggleIsEnabledProperty = DependencyProperty.Register("MustToggleIsEnabled", typeof(bool), typeof(EventToCommand), new PropertyMetadata(false, delegate(DependencyObject s, DependencyPropertyChangedEventArgs e)
    //        {
    //            EventToCommand command = s as EventToCommand;
    //            if ((command != null) && (command.AssociatedObject != null))
    //            {
    //                command.EnableDisableElement();
    //            }
    //        }));
    //    }

    //    private bool AssociatedElementIsDisabled()
    //    {
    //        FrameworkElement associatedObject = this.GetAssociatedObject();
    //        return ((associatedObject != null) && !associatedObject.IsEnabled);
    //    }

    //    private void EnableDisableElement()
    //    {
    //        FrameworkElement associatedObject = this.GetAssociatedObject();
    //        if (associatedObject != null)
    //        {
    //            ICommand command = this.GetCommand();
    //            if (this.MustToggleIsEnabledValue && (command != null))
    //            {
    //                associatedObject.IsEnabled = command.CanExecute(this.CommandParameterValue);
    //            }
    //        }
    //    }

    //    private FrameworkElement GetAssociatedObject()
    //    {
    //        return base.AssociatedObject;
    //    }

    //    private ICommand GetCommand()
    //    {
    //        return this.Command;
    //    }

    //    public void Invoke()
    //    {
    //        this.Invoke(null);
    //    }

    //    protected override void Invoke(object parameter)
    //    {
    //        if (!this.AssociatedElementIsDisabled())
    //        {
    //            ICommand command = this.GetCommand();
    //            object commandParameterValue = this.CommandParameterValue;
    //            if ((commandParameterValue == null) && this.PassEventArgsToCommand)
    //            {
    //                commandParameterValue = parameter;
    //            }
    //            if ((command != null) && command.CanExecute(commandParameterValue))
    //            {
    //                command.Execute(commandParameterValue);
    //            }
    //        }
    //    }

    //    protected override void OnAttached()
    //    {
    //        base.OnAttached();
    //        this.EnableDisableElement();
    //    }

    //    private void OnCommandCanExecuteChanged(object sender, EventArgs e)
    //    {
    //        this.EnableDisableElement();
    //    }

    //    private static void OnCommandChanged(EventToCommand element, DependencyPropertyChangedEventArgs e)
    //    {
    //        if (element != null)
    //        {
    //            if (e.OldValue != null)
    //            {
    //                ((ICommand)e.OldValue).CanExecuteChanged -= new EventHandler(element.OnCommandCanExecuteChanged);
    //            }
    //            ICommand newValue = (ICommand)e.NewValue;
    //            if (newValue != null)
    //            {
    //                newValue.CanExecuteChanged += new EventHandler(element.OnCommandCanExecuteChanged);
    //            }
    //            element.EnableDisableElement();
    //        }
    //    }

    //    // Properties
    //    public ICommand Command
    //    {
    //        get
    //        {
    //            return (ICommand)base.GetValue(CommandProperty);
    //        }
    //        set
    //        {
    //            base.SetValue(CommandProperty, value);
    //        }
    //    }

    //    public object CommandParameter
    //    {
    //        get
    //        {
    //            return base.GetValue(CommandParameterProperty);
    //        }
    //        set
    //        {
    //            base.SetValue(CommandParameterProperty, value);
    //        }
    //    }

    //    public object CommandParameterValue
    //    {
    //        get
    //        {
    //            return (this._commandParameterValue ?? this.CommandParameter);
    //        }
    //        set
    //        {
    //            this._commandParameterValue = value;
    //            this.EnableDisableElement();
    //        }
    //    }

    //    public bool MustToggleIsEnabled
    //    {
    //        get
    //        {
    //            return (bool)base.GetValue(MustToggleIsEnabledProperty);
    //        }
    //        set
    //        {
    //            base.SetValue(MustToggleIsEnabledProperty, value);
    //        }
    //    }

    //    public bool MustToggleIsEnabledValue
    //    {
    //        get
    //        {
    //            return (!this._mustToggleValue.HasValue ? this.MustToggleIsEnabled : this._mustToggleValue.Value);
    //        }
    //        set
    //        {
    //            this._mustToggleValue = new bool?(value);
    //            this.EnableDisableElement();
    //        }
    //    }

    //    public bool PassEventArgsToCommand
    //    {
    //        get
    //        {
    //            return this._passEventArgsToCommand;
    //        }
    //        set
    //        {
    //            this._passEventArgsToCommand = value;
    //        }
    //    }
    //}


    public class EventToCommand : TriggerAction<DependencyObject>
    {
        /// <summary>
        /// Identifies the <see cref="CommandParameter" /> dependency property
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
            "CommandParameter",
            typeof(object),
            typeof(EventToCommand),
            new PropertyMetadata(
                null,
                (s, e) =>
                {
                    var sender = s as EventToCommand;
                    if (sender == null)
                    {
                        return;
                    }

                    if (sender.AssociatedObject == null)
                    {
                        return;
                    }

                    sender.EnableDisableElement();
                }));

        /// <summary>
        /// Identifies the <see cref="Command" /> dependency property
        /// </summary>
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
            "Command",
            typeof(ICommand),
            typeof(EventToCommand),
            new PropertyMetadata(
                null,
                (s, e) => OnCommandChanged(s as EventToCommand, e)));

        /// <summary>
        /// Identifies the <see cref="MustToggleIsEnabled" /> dependency property
        /// </summary>
        public static readonly DependencyProperty MustToggleIsEnabledProperty = DependencyProperty.Register(
            "MustToggleIsEnabled",
            typeof(bool),
            typeof(EventToCommand),
            new PropertyMetadata(
                false,
                (s, e) =>
                {
                    var sender = s as EventToCommand;
                    if (sender == null)
                    {
                        return;
                    }

                    if (sender.AssociatedObject == null)
                    {
                        return;
                    }

                    sender.EnableDisableElement();
                }));

        private object _commandParameterValue;

        private bool? _mustToggleValue;

        /// <summary>
        /// Gets or sets the ICommand that this trigger is bound to. This
        /// is a DependencyProperty.
        /// </summary>
        public ICommand Command
        {
            get
            {
                return (ICommand)GetValue(CommandProperty);
            }

            set
            {
                SetValue(CommandProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets an object that will be passed to the <see cref="Command" />
        /// attached to this trigger. This is a DependencyProperty.
        /// </summary>
        public object CommandParameter
        {
            get
            {
                return this.GetValue(CommandParameterProperty);
            }

            set
            {
                SetValue(CommandParameterProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets an object that will be passed to the <see cref="Command" />
        /// attached to this trigger. This property is here for compatibility
        /// with the Silverlight version. This is NOT a DependencyProperty.
        /// For databinding, use the <see cref="CommandParameter" /> property.
        /// </summary>
        public object CommandParameterValue
        {
            get
            {
                return this._commandParameterValue ?? this.CommandParameter;
            }

            set
            {
                _commandParameterValue = value;
                EnableDisableElement();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the attached element must be
        /// disabled when the <see cref="Command" /> property's CanExecuteChanged
        /// event fires. If this property is true, and the command's CanExecute 
        /// method returns false, the element will be disabled. If this property
        /// is false, the element will not be disabled when the command's
        /// CanExecute method changes. This is a DependencyProperty.
        /// </summary>
        public bool MustToggleIsEnabled
        {
            get
            {
                return (bool)this.GetValue(MustToggleIsEnabledProperty);
            }

            set
            {
                SetValue(MustToggleIsEnabledProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the attached element must be
        /// disabled when the <see cref="Command" /> property's CanExecuteChanged
        /// event fires. If this property is true, and the command's CanExecute 
        /// method returns false, the element will be disabled. This property is here for
        /// compatibility with the Silverlight version. This is NOT a DependencyProperty.
        /// For databinding, use the <see cref="MustToggleIsEnabled" /> property.
        /// </summary>
        public bool MustToggleIsEnabledValue
        {
            get
            {
                return this._mustToggleValue == null
                           ? this.MustToggleIsEnabled
                           : this._mustToggleValue.Value;
            }

            set
            {
                _mustToggleValue = value;
                EnableDisableElement();
            }
        }

        /// <summary>
        /// Called when this trigger is attached to a FrameworkElement.
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();
            EnableDisableElement();
        }

#if SILVERLIGHT
        private Control GetAssociatedObject()
        {
            return AssociatedObject as Control;
        }
#else
        /// <summary>
        /// This method is here for compatibility
        /// with the Silverlight version.
        /// </summary>
        /// <returns>The FrameworkElement to which this trigger
        /// is attached.</returns>
        private FrameworkElement GetAssociatedObject()
        {
            return AssociatedObject as FrameworkElement;
        }
#endif

        /// <summary>
        /// This method is here for compatibility
        /// with the Silverlight 3 version.
        /// </summary>
        /// <returns>The command that must be executed when
        /// this trigger is invoked.</returns>
        private ICommand GetCommand()
        {
            return Command;
        }

        /// <summary>
        /// Specifies whether the EventArgs of the event that triggered this
        /// action should be passed to the bound RelayCommand. If this is true,
        /// the command should accept arguments of the corresponding
        /// type (for example RelayCommand&lt;MouseButtonEventArgs&gt;).
        /// </summary>
        public bool PassEventArgsToCommand
        {
            get;
            set;
        }

        /// <summary>
        /// Provides a simple way to invoke this trigger programatically
        /// without any EventArgs.
        /// </summary>
        public void Invoke()
        {
            Invoke(null);
        }

        /// <summary>
        /// Executes the trigger.
        /// <para>To access the EventArgs of the fired event, use a RelayCommand&lt;EventArgs&gt;
        /// and leave the CommandParameter and CommandParameterValue empty!</para>
        /// </summary>
        /// <param name="parameter">The EventArgs of the fired event.</param>
        protected override void Invoke(object parameter)
        {
            if (AssociatedElementIsDisabled())
            {
                return;
            }

            var command = GetCommand();
            var commandParameter = CommandParameterValue;

            if (commandParameter == null
                && PassEventArgsToCommand)
            {
                commandParameter = parameter;
            }

            if (command != null
                && command.CanExecute(commandParameter))
            {
                command.Execute(commandParameter);
            }
        }

        private static void OnCommandChanged(
            EventToCommand element,
            DependencyPropertyChangedEventArgs e)
        {
            if (element == null)
            {
                return;
            }

            if (e.OldValue != null)
            {
                ((ICommand)e.OldValue).CanExecuteChanged -= element.OnCommandCanExecuteChanged;
            }

            var command = (ICommand)e.NewValue;

            if (command != null)
            {
                command.CanExecuteChanged += element.OnCommandCanExecuteChanged;
            }

            element.EnableDisableElement();
        }

        private bool AssociatedElementIsDisabled()
        {
            var element = GetAssociatedObject();

            return AssociatedObject == null
                || (element != null
                   && !element.IsEnabled);
        }

        private void EnableDisableElement()
        {
            var element = GetAssociatedObject();

            if (element == null)
            {
                return;
            }

            var command = this.GetCommand();

            if (this.MustToggleIsEnabledValue
                && command != null)
            {
                element.IsEnabled = command.CanExecute(this.CommandParameterValue);
            }
        }

        private void OnCommandCanExecuteChanged(object sender, EventArgs e)
        {
            EnableDisableElement();
        }
    }
}
